package cn.lyjuan.payment.third.base.pay.chl.impl.ali.util;

import cn.lyjuan.base.exception.impl.BaseCode;
import cn.lyjuan.base.exception.util.ErrUtils;
import cn.lyjuan.base.util.RandomUtils;
import cn.lyjuan.base.util.StringUtils;
import cn.lyjuan.payment.third.base.pay.chl.impl.ali.bean.AliChlCnf;
import cn.lyjuan.payment.third.base.pay.chl.impl.ali.bean.AliPayMapRes;
import cn.lyjuan.payment.third.base.pay.chl.impl.ali.bean.AliPayReq;
import cn.lyjuan.payment.third.base.pay.chl.impl.ali.bean.AlipayTransferReq;
import cn.lyjuan.payment.third.base.pay.cst.PayConst;
import cn.lyjuan.payment.third.base.pay.enums.SimpleChlEnum;
import cn.lyjuan.payment.third.base.pay.vo.req.PayReq;
import cn.lyjuan.payment.third.base.pay.vo.req.TransferReq;
import cn.lyjuan.payment.third.base.pay.vo.res.BaseRes;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayResponse;
import com.alipay.api.CertAlipayRequest;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayFundTransUniTransferModel;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.domain.Participant;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.*;
import com.alipay.api.response.*;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * 支付宝手机端SDK支付提交工具
 * Created by ly on 2015/2/4.
 */
@Slf4j
public class AlipaySubmit {
    /**
     * 支付宝网关
     */
    public static final String ALI_GATEWAY = "https://openapi.alipay.com/gateway.do";
    /**
     * 支付宝沙箱环境
     */
    public static final String ALI_GATEWAY_DEBUG = "https://openapi.alipaydev.com/gateway.do";
    /**
     * 支付宝交易字段timestamp格式
     */
    private static DateTimeFormatter DTF = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    /**
     * 检查交易请求参数是否合法
     *
     * @param params
     * @return
     */
    public static BaseRes checkPayReq(AliPayReq params) {
        return StringUtils.isNull(params.getService())
                ? new BaseRes(false) : new BaseRes();
    }

    /**
     * 获取网站支付表单
     *
     * @param order 订单
     * @param cnf   配置
     * @param isDev 是否是开发模式
     * @return AliPayMapRes
     */
    public static AliPayMapRes getWebQrCodeForm(PayReq<AliPayReq> order, AliChlCnf cnf, boolean isDev) {
        DefaultAlipayClient alipayClient;
        try {
            alipayClient = AlipaySubmit.makeCertAlipayClient(cnf.getCertPath(), cnf, isDev);
            AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
            Map<String, String> map = new HashMap<>();
            map.put("subject", order.getGoods());
            map.put("out_trade_no", RandomUtils.uuid());
            map.put("total_amount", Double.toString(order.getAmt() / 100.0));
            map.put("product_code", "FAST_INSTANT_TRADE_PAY");
            request.setBizContent(JSONObject.toJSONString(map));
            request.setNotifyUrl(order.getNotifyUrl());
            AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
            log.info("response {}", response.getBody());
            return new AliPayMapRes(response.getBody());
        } catch (AlipayApiException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 检查转账参数是否合法
     *
     * @param params 参数
     * @return BaseRes
     */
    public static BaseRes checkTransferReq(AlipayTransferReq params) {
        if (params.isCheckName() && StringUtils.isNull(params.getRealName())) {
            return new BaseRes(false);
        }
        return StringUtils.isNull(params.getService())
                ? new BaseRes(false) : new BaseRes();
    }

    /**
     * 创建支付宝手机交易请求参数
     *
     * @param order   订单信息
     * @param cnf     支付平台配置信息
     * @param isDebug 是否沙箱环境
     * @return
     */
    public static AliPayMapRes createTradeExec(PayReq<AliPayReq> order, AliChlCnf cnf, boolean isDebug) {
        DefaultAlipayClient alipayClient;
        try {
            alipayClient = makeCertAlipayClient(cnf.getCertPath(), cnf, isDebug);
            //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称：alipay.trade.app.pay
            AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
            //SDK已经封装掉了公共参数，这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
            AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
            model.setBody(order.getGoods());
            model.setSubject(order.getGoods());
            model.setOutTradeNo(order.getLocalNo());
            model.setTotalAmount(Double.toString(order.getAmt() / 100.0));
            model.setProductCode("QUICK_MSECURITY_PAY");
            request.setBizModel(model);
            request.setNotifyUrl(order.getNotifyUrl());
            //这里和普通的接口调用不同，使用的是sdkExecute
            AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
            log.info("response ==> {}", StringUtils.toStr(response, AlipayTradeAppPayResponse.class, AlipayResponse.class));
            System.out.println("ALI response ------------->" + JSONObject.toJSONString(response));
            return new AliPayMapRes(response.getBody());
        } catch (AlipayApiException e) {
            throw new RuntimeException(e);
        }
        // todo 修改
        //return null;
    }

    /**
     * 执行转账
     *
     * @param order   参数
     * @param cnf     配置
     * @param isDebug isDebug
     * @return AliPayMapRes
     */
    public static AliPayMapRes transferExec(TransferReq<AlipayTransferReq> order, AliChlCnf cnf, boolean isDebug) {
        DefaultAlipayClient alipayClient;
        try {
            alipayClient = makeCertAlipayClient(cnf.getCertPath(), cnf, isDebug);
            AlipayFundTransUniTransferRequest request = new AlipayFundTransUniTransferRequest();
            AlipayFundTransUniTransferModel model = new AlipayFundTransUniTransferModel();
            model.setOutBizNo(order.getLocalNo());
            model.setTransAmount(Double.toString(order.getAmt() / 100.0));
            model.setProductCode("TRANS_ACCOUNT_NO_PWD");
            model.setBizScene("DIRECT_TRANSFER");
            model.setOrderTitle(order.getTitle());
            Participant participant = new Participant();
            if (order.getParams().isCheckName()) {
                participant.setIdentityType("ALIPAY_LOGON_ID");
                participant.setName(order.getParams().getRealName());
            } else {
                participant.setIdentityType("ALIPAY_USER_ID");
            }
            participant.setIdentity(order.getIdentity());
            model.setPayeeInfo(participant);
            model.setRemark(order.getRemark());
            request.setBizModel(model);
            AlipayFundTransUniTransferResponse response = null;
            for (int i = 0; i < 3; i++) {
                response = alipayClient.certificateExecute(request);
                if ("10000".equals(response.getCode()) && "SUCCESS".equals(response.getStatus())) {
                    return new AliPayMapRes(true, response.getBody());
                }
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    log.error("Thread sleep error: [{}]", e.getMessage());
                    throw new RuntimeException(e);
                }
            }
            log.error("alipay transfer error: retry three times over: orderNo: [{}] identity: [{}]", model.getOutBizNo(), participant.getIdentity());
            return new AliPayMapRes(response.getBody());
        } catch (AlipayApiException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 签名
     *
     * @param params     参数
     * @param privateKey 私钥
     * @return String
     */
    public static String signForPayCtl(Map<String, String> params, String privateKey) {
        String signType = params.get("sign_type");
        signType = signType == null || signType.equals("") ? "RSA2" : signType;
        String charset = params.get("charset");
        charset = charset == null || charset.equals("") ? "UTF-8" : charset;
        String waitSignStr = createLinkString(params);
        try {
            String sign = AlipaySignature.rsaSign(waitSignStr, privateKey, charset, signType);
            params.put("sign", sign);
            return buildOrderParam(params);
        } catch (AlipayApiException e) {
            log.error("alipay request sign error: params:[{}]  msg: [{}]", params, e.getMessage());
            ErrUtils.appThrow(BaseCode.FAILED);
        }
        return null;
    }

    /**
     * 构造支付订单参数信息
     *
     * @param map 支付订单参数
     * @return String
     */
    public static String buildOrderParam(Map<String, String> map) {
        List<String> keys = new ArrayList<>(map.keySet());

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < keys.size() - 1; i++) {
            String key = keys.get(i);
            String value = map.get(key);
            sb.append(buildKeyValue(key, value, true));
            sb.append("&");
        }
        String tailKey = keys.get(keys.size() - 1);
        String tailValue = map.get(tailKey);
        sb.append(buildKeyValue(tailKey, tailValue, true));
        return sb.toString();
    }

    /**
     * 拼接键值对
     *
     * @param key      key
     * @param value    value
     * @param isEncode isEncode
     * @return String
     */
    private static String buildKeyValue(String key, String value, boolean isEncode) {
        StringBuilder sb = new StringBuilder();
        sb.append(key);
        sb.append("=");
        if (isEncode) {
            try {
                sb.append(URLEncoder.encode(value, "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                sb.append(value);
            }
        } else {
            sb.append(value);
        }
        return sb.toString();
    }

    /**
     * 授权码 换 AccessToken
     *
     * @param authCode 授权码
     * @return AlipaySystemOauthTokenResponse
     */
    public static AlipaySystemOauthTokenResponse authCodeExchangeAccessToken(DefaultAlipayClient certAlipayClient, String authCode) {
        AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
        request.setGrantType("authorization_code");
        request.setCode(authCode);
        AlipaySystemOauthTokenResponse response = null;
        try {
            response = certAlipayClient.certificateExecute(request);
        } catch (AlipayApiException e) {
            log.error("authCodeExchangeAccessToken error: [{}]", e.getMessage());
        }
        return response;
    }

    /**
     * accessToken 换 用户共享信息
     *
     * @param accessToken accessToken
     * @return AlipayUserInfoShareResponse
     */
    public static AlipayUserInfoShareResponse accessTokenExchangeUserShareInfo(DefaultAlipayClient certAlipayClient, String accessToken) {
        AlipayUserInfoShareRequest request = new AlipayUserInfoShareRequest();
        AlipayUserInfoShareResponse response = null;
        try {
            response = certAlipayClient.certificateExecute(request, accessToken);
        } catch (AlipayApiException e) {
            log.error("accessTokenExchangeUserShareInfo error: [{}]", e.getMessage());
        }
        return response;
    }

    public static DefaultAlipayClient makeCertAlipayClient(String path, AliChlCnf cnf, boolean isDebug) {
        CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
        certAlipayRequest.setServerUrl(isDebug ? ALI_GATEWAY_DEBUG : ALI_GATEWAY);
        log.debug("AliPaySubmit makeCertAlipayClient if in debug: [{}] mode, serverUrl: [{}]", isDebug, certAlipayRequest.getServerUrl());
        certAlipayRequest.setAppId(cnf.getAppId());
        certAlipayRequest.setPrivateKey(cnf.getRsaPri());
        certAlipayRequest.setFormat("json");
        certAlipayRequest.setCharset(PayConst.CHARSET);
        certAlipayRequest.setSignType("RSA2");
        String prefix = getPrefixPath(path) + SimpleChlEnum.ALI.name().toLowerCase();
        certAlipayRequest.setCertPath(prefix + "/appCertPublicKey_" + cnf.getAppId() + ".crt");
        certAlipayRequest.setAlipayPublicCertPath(prefix + "/alipayCertPublicKey_RSA2.crt");
        certAlipayRequest.setRootCertPath(prefix + "/alipayRootCert.crt");
        try {
            return new DefaultAlipayClient(certAlipayRequest);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 生成待签名串
     *
     * @param paramMap 参数
     * @return String
     */
    public static String createLinkString(Map<String, String> paramMap) {
        paramMap.remove("sign");
        List<String> keys = new ArrayList<>(paramMap.keySet());
        Collections.sort(keys);
        String prestr = "";
        for (String key : keys) {
            String value = paramMap.get(key);
            if (value == null || "".equals(value)) {
                continue;
            }
            prestr += key + "=" + value + "&";
        }
        // 拼接时，不包括最后一个&字符
        prestr = prestr.substring(0, prestr.length() - 1);
        return prestr;
    }

    /**
     * 获取证书路径
     *
     * @param path path
     * @return String
     */
    public static String getPrefixPath(String path) {
        File file = new File(path);
        if (file.isAbsolute()) {
            return file.getAbsolutePath() + "/";
        }
        String clsPath = AlipaySubmit.class.getClassLoader().getResource("./").getPath();
        return clsPath.replace("test-classes", "classes") + path;
    }
}
